package com.tougutips.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.tougutips.common.core.constant.MsgConstants;
import com.tougutips.common.core.constant.TokenConstants;
import com.tougutips.common.redis.service.RedisService;
import com.tougutips.common.utils.JWTUtil;
import com.tougutips.common.utils.StringUtil;
import com.tougutips.gateway.config.IgnoreWhiteProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 前台应用网关
 */
@Component
@Slf4j
public class AuthFilter implements GlobalFilter, Ordered {
    @Autowired
    private RedisService redisService;
    @Autowired
    private IgnoreWhiteProperties ignoreWhiteProperties;

    /**
     * 过滤Token
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        log.info("开始过滤Token,{}", request);
        // 白名单过滤
        if (StringUtil.matches(request.getURI().getPath(), ignoreWhiteProperties.getWhites())) {
            return chain.filter(exchange);
        }
        String token = getToken(request);
        // 判断请求头是否有token
        if (StringUtil.isEmpty(token)) {
            return errResponse(response, MsgConstants.LOGIN_USER);
        }
        Map<String, Object> payload = JWTUtil.getAccount(token);
        String account = (String)payload.get(TokenConstants.ACCOUNT);
        // 从redis中获取token
        Object redis_token = redisService.getCacheObject(account);
        // 如果redis_token为null,表示redis过期了
        if (StringUtil.isEmpty(redis_token)) {
            return errResponse(response, MsgConstants.USERINFO_TIMEOUT);
        }
        try {
            // 验证token是否过期
            JWTUtil.verify(token);
            // 验证token是否与redis的token一致
            if (!redis_token.equals(token)) {
                return errResponse(response, MsgConstants.TOKEN_FAIL);
            }
        } catch (JWTVerificationException e) {
            // token过期了,重新刷新token,放入redis,并返回前端
            token = JWTUtil.sign(payload);
            redisService.setCacheObject(account,token,7L, TimeUnit.DAYS);
            // 9999状态码表示重新刷新Token
            return errResponse(response,9999,MsgConstants.TOKEN_REFRESH,token);
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }

    public String getToken(ServerHttpRequest request) {
        return request.getHeaders().getFirst(TokenConstants.AUTHENTICATION);
    }

    public Mono<Void> errResponse(ServerHttpResponse response, String msg) {
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        AjaxResult result = AjaxResult.error(HttpStatus.UNAUTHORIZED.value(), msg);
        DataBuffer dataBuffer = response.bufferFactory().wrap(JSONObject.toJSONString(result).getBytes());
        return response.writeWith(Mono.just(dataBuffer));
    }
    public Mono<Void> errResponse(ServerHttpResponse response,int code,String msg) {
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        AjaxResult result = AjaxResult.error(code, msg);
        DataBuffer dataBuffer = response.bufferFactory().wrap(JSONObject.toJSONString(result).getBytes());
        return response.writeWith(Mono.just(dataBuffer));
    }
    public Mono<Void> errResponse(ServerHttpResponse response,int code,String msg,Object data) {
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        AjaxResult result = AjaxResult.error(code, msg);
        result.put(TokenConstants.DATA,data);
        DataBuffer dataBuffer = response.bufferFactory().wrap(JSONObject.toJSONString(result).getBytes());
        return response.writeWith(Mono.just(dataBuffer));
    }
}
